import type { PatentIdentifier } from '@/types'
import {
  findIdentifier, getDate, syntaxError, typeError,
} from '@/utils/retrieval-sql/utils'
import type {
  Expression, Identifier, Literal, Node, Visitor,
} from '@/types/retrieval-sql'

/**
 * 对初始的结构树进行语义分析，如操作符进行限定，查询内容格式（如日期）是否正确等
 */

/**
 * 期间转成 BETWEEN 节点
 */
function identifierLiteralVisitor(identifier: PatentIdentifier, literal: Literal): Expression | Literal {
  const variant = identifier.type || literal.variant
  if (['number', 'date'].includes(variant)) {
    if ((variant === 'number' && literal.value.includes('-')) || literal.value.includes(':') || literal.value.includes('：')) {
      const symbol = variant === 'number' && literal.value.includes('-') ? '-' : (literal.value.includes(':') ? ':' : '：')
      const splits = literal.value.split(symbol)
      return {
        type: 'Expression',
        variant: 'operation',
        operation: 'BETWEEN',
        left: {
          type: 'Literal',
          variant,
          value: splits[0],
          start: literal.start,
          end: literal.end,
        },
        right: {
          type: 'Literal',
          variant,
          value: splits[1],
          start: literal.start,
          end: literal.end,
        },
        start: literal.start,
        end: literal.end,
      }
    }
    if (variant === 'date') {
      return {
        type: 'Expression',
        variant: 'operation',
        operation: 'BETWEEN',
        left: {
          type: 'Literal',
          variant,
          value: literal.value,
          start: literal.start,
          end: literal.end,
        },
        right: {
          type: 'Literal',
          variant,
          value: literal.value,
          start: literal.start,
          end: literal.end,
        },
        start: literal.start,
        end: literal.end,
      }
    }
  }
  return literal
}

/**
 * between 区间值处理, 1. 限定数字和日期 2.日期期间格式化
 */
function betweenVisitor(node: Expression) {
  if (node.operation === 'BETWEEN') {
    if (node.left?.type !== 'Literal' || node.right?.type !== 'Literal' || node.left.variant !== node.right.variant || !['number', 'date'].includes(node.left.variant)) {
      throw typeError({
        message: '内部错误，BETWEEN 转换错误',
        start: node.start,
        end: node.end,
      })
    }
    if (node.left.variant === 'date') {
      const start = getDate(node.left.value)
      const end = getDate(node.right.value, true)
      if (!start || !end) {
        throw typeError({
          message: '无效的日期',
          start: node.start,
          end: node.end,
        })
      }
      node.left.value = start
      node.right.value = end
    }
  }
}

/**
 * 比较运算符处理
 */
function operationVisitor(node: Identifier, parent: Expression<Identifier, Expression | Literal>, identifiers: PatentIdentifier[]) {
  const right = parent.right
  if (!right) {
    throw typeError({
      message: '内部错误，查询值为空',
      start: parent.start,
      end: parent.end,
    })
  }

  const identifier = findIdentifier(identifiers, node.name)
  if (!identifier) {
    throw syntaxError({
      message: `无效的字段 "${node.name}"`,
      start: node.start,
      end: node.end,
    })
  }
  node.name = identifier.key

  const identifierType = identifier.type || 'text'

  // 验证比较运算符和查询值关系类型
  if (['>', '>=', '<', '<='].includes(parent.operation)) {
    // 检查区间
    if (['number', 'date'].includes(identifierType)) {
      if (right.type === 'Literal') {
        if (right.value.includes(':') || right.value.includes('：')) {
          throw syntaxError({
            message: `"${parent.operation}"运算符不支持区间":"`,
            start: parent.start,
            end: parent.end,
          })
        }
        right.variant = identifierType
        return
      }

      if (right.type === 'Expression' && (right.variant === 'list' || right.operation === 'BETWEEN')) {
        throw syntaxError({
          message: `"${parent.operation}"运算符不支持多个值`,
          start: parent.start,
          end: parent.end,
        })
      }

      throw typeError({
        message: `内部错误，不支持的类型 ${right?.type}`,
        start: parent.start,
        end: parent.end,
      })
    }
    throw syntaxError({
      message: `"${parent.operation}"运算符不支持文本类型`,
      start: parent.start,
      end: parent.end,
    })
  }

  if (parent.operation === '=') {
    if (parent.right?.type === 'Literal') {
      right.variant = identifierType
      parent.right = identifierLiteralVisitor(identifier, parent.right)
      return
    } if (right.type === 'Expression' && right.variant === 'list') {
      const expression: Node[] = []
      for (const it of right.expression!) {
        if (it.type === 'Literal') {
          it.variant = identifierType
          expression.push(identifierLiteralVisitor(identifier, it))
        } else {
          throw typeError({
            message: `内部错误，非法的类型 ${it.type}`,
            start: parent.start,
            end: parent.end,
          })
        }
      }
      right.expression = expression
      return
    }
    throw typeError({
      message: `内部错误，非法的类型 ${right.type}`,
      start: parent.start,
      end: parent.end,
    })
  }
  throw syntaxError({
    message: `"${parent.operation}"运算符使用错误`,
    start: parent.start,
    end: parent.end,
  })
}

/**
 * 校验参数值的格式
 */
function literalValueVisitor(node: Literal, parent: Expression) {
  switch (node.variant) {
    case 'number':
      if (!Number.isFinite(Number(node.value))) {
        throw typeError({
          message: '非法的数字',
          start: node.start,
          end: node.end,
        })
      }
      if (parent.left?.type === 'Identifier' && parent.left.name === 'patent_value') {
        if (Number(node.value) < 0 || Number(node.value) > 100) {
          throw typeError({
            message: '专利价值度数值错误，需要大于 0 小于 100',
            start: node.start,
            end: node.end,
          })
        }
      }
      break
    case 'date':
      node.value = getDate(node.value, parent.operation === '<=' || parent.operation === '>') || ''
      if (!node.value) {
        throw typeError({
          message: '非法的日期',
          start: node.start,
          end: node.end,
        })
      }

      break
    case 'text':
      // （2）通配符不支持放在关键词的开头使用；示例：*olor、?olor；
      if (node.value.startsWith('*') || node.value.startsWith('?')) {
        throw typeError({
          message: '通配符(* ?)不支持放在关键词的开头使用',
          start: node.start,
          end: node.end,
        })
      }

      if (node.value.includes('*') && node.value.includes('?')) {
        throw typeError({
          message: '通配符(* ?)不支持在一个关键词中同时使用',
          start: node.start,
          end: node.end,
        })
      }

      break
    case 'term':
      break
    default:
      throw typeError({
        message: '内部错误，未知的类型',
        start: node.start,
        end: node.end,
      })
  }
}

export default function createVisitor(identifiers: PatentIdentifier[]) {
  return <Visitor>{
    Expression(node): void {
      betweenVisitor(node)
    },
    Identifier(node, parent): void {
      operationVisitor(node, parent, identifiers)
    },
    Literal(node, parent): void {
      literalValueVisitor(node, parent)
    },
  }
}
